//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <wchar.h>

#include <aura.h>
#include <auraplus.h>
#include <aura_wchar.h>

EXTERN_C void * CDECL
Aura_malloc(
    /* [in] */ Aura_Int32 size)
{
    return malloc(size);
}

EXTERN_C void * CDECL
Aura_realloc(
    /* [in] */ void * p,
    /* [in] */ Aura_Int32 size)
{
    return realloc(p, size);
}

EXTERN_C void CDECL
Aura_free(
    /* [in] */ void * p)
{
    free(p);
    return ;
}

EXTERN_C Aura_Int32 __cdecl
Aura_putws(
      /* [in]  */ const Aura_PWChar wstr)
{
    Aura_Int32 ret;
    Aura_PWChar s;

    s=wstr;
    ret = 0;

    while(*s){
        putwchar((wchar_t)*s++);
        ret++;
    }
    putchar('\n');

    return ++ret;
}

#define MAXBUF (sizeof(long int) * 8)
static int  doprnt_truncates = 0;
#define Internal_IsDigit(c) ((c) >= '0' && (c) <= '9')
#define Ctod(c) ((c) - '0')

static void
doprnt (const char* fmt, va_list args, int radix,
        bool ( *putc)(void*, int), void* putc_arg)
{
    int     length;
    int     prec;
    int     ladjust;
    char    padc;
    long    n;
    unsigned long   u;
    int     plus_sign;
    int     sign_char;
    int     altfmt, truncate;
    int     base;
    int     X = 0;
    int     c;

    while (*fmt != '\0')
    {
        if (*fmt != '%')
        {
            if (!(*putc) (putc_arg, *fmt++))
                goto L_EXIT;
            continue;
        }

        fmt++;

        length = 0;
        prec = -1;
        ladjust = 0;
        padc = ' ';
        plus_sign = 0;
        sign_char = 0;
        altfmt = 0;

        while (1)
        {
            if (*fmt == '#')
            {
                altfmt = 1;
                fmt++;
            }
            else if (*fmt == '-')
            {
                ladjust = 1;
                fmt++;
            }
            else if (*fmt == '+')
            {
                plus_sign = '+';
                fmt++;
            }
            else if (*fmt == ' ')
            {
                if (plus_sign == 0)
                    plus_sign = ' ';
                fmt++;
            }
            else
                break;
        }

        if (*fmt == '0')
        {
            padc = '0';
            fmt++;
        }

        if (Internal_IsDigit (*fmt))
        {
            while (Internal_IsDigit (*fmt))
                length = 10 * length + Ctod (*fmt++);
        }
        else if (*fmt == '*')
        {
            length = va_arg (args, int);
            fmt++;
            if (length < 0)
            {
                ladjust = !ladjust;
                length = -length;
            }
        }

        if (*fmt == '.')
        {
            fmt++;
            if (Internal_IsDigit (*fmt))
            {
                prec = 0;
                while (Internal_IsDigit (*fmt))
                    prec = 10 * prec + Ctod (*fmt++);
            }
            else if (*fmt == '*') {
                prec = va_arg (args, int);
                fmt++;
            }
        }

        if (*fmt == 'l')
            fmt++;  /* need it if sizeof(int) < sizeof(long) */

        truncate = 0;

        switch (*fmt)
        {
        case 'c':
            c = va_arg(args, int);
            if (!(*putc)(putc_arg, c))
                goto L_EXIT;
            break;

        case 's':
            {
                char *p;
                char *p2;
                register Aura_WChar* tmp;
                register Aura_WChar* tmp2;

                if (prec == -1)
                    prec = 0x7fffffff;  /* MAXINT */

                p = va_arg (args, char *);

                if (p == (char*)0)
                    p = "";

                if (length > 0 && !ladjust)
                {
                    n = 0;
                    p2 = p;

                    for (; *p != '\0' && n < prec; p++)
                        n++;

                    p = p2;

                    while (n < length)
                    {
                        if (!(*putc)(putc_arg, ' '))
                            goto L_EXIT;
                        n++;
                    }
                }

                n = strlen (p);

                tmp = (Aura_WChar*)malloc ((n + 1) * sizeof (Aura_WChar));
                if (tmp == NULL)
                    goto L_EXIT;

                n = Aura_Mbstowcs(tmp,p,n+1);
                if (n <= 0)
                {
                    free (tmp);
                    goto L_EXIT;
                }
                n = 0;
                tmp2 = tmp;

                while (*tmp2 != '\0')
                {
                    if (++n > prec)
                        break;

                    if (!(*putc)(putc_arg, *tmp2++))
                        goto L_EXIT;
                }
                free (tmp);

                if (n < length && ladjust)
                {
                    while (n < length)
                    {
                        if (!(*putc)(putc_arg, ' '))
                            goto L_EXIT;
                        n++;
                    }
                }

                break;
            }

        case 'S':
            {
                register Aura_WChar *p;
                register Aura_WChar *p2;

                if (prec == -1)
                    prec = 0x7fffffff;  /* MAXINT */

                p = va_arg(args, Aura_WChar *);

                if (p == (Aura_WChar*)0)
                    p = (Aura_WChar*)"\0";

                if (length > 0 && !ladjust)
                {
                    n = 0;
                    p2 = p;

                    for (; *p != '\0' && n < prec; p++)
                        n++;

                    p = p2;

                    while (n < length)
                    {
                        (*putc)(putc_arg, ' ');
                        n++;
                    }
                }

                n = 0;

                while (*p != (Aura_WChar)'\0')
                {
                    if (++n > prec)
                        break;

                    if (!(*putc)(putc_arg, *p++))
                        goto L_EXIT;
                }

                if (n < length && ladjust)
                {
                    while (n < length)
                    {
                        if (!(*putc)(putc_arg, ' '))
                            goto L_EXIT;
                        n++;
                    }
                }

                break;
            }

        case 'o':
            truncate = doprnt_truncates;
        case 'O':
            base = 8;
            goto print_unsigned;

        case 'd':
        case 'i':
            truncate = doprnt_truncates;
        case 'D':
            base = 10;
            goto print_signed;

        case 'u':
            truncate = doprnt_truncates;
        case 'U':
            base = 10;
            goto print_unsigned;

        case 'p':
            padc = '0';
            length = 8;
            /*
            * We do this instead of just setting altfmt to TRUE
            * because we want 0 to have a 0x in front, and we want
            * eight digits after the 0x -- not just 6.
            */
            if (!(*putc)(putc_arg, '0'))
                goto L_EXIT;
            if (!(*putc)(putc_arg, 'x'))
                goto L_EXIT;
        case 'x':
            X = 0;
            truncate = doprnt_truncates;
            base = 16;
            goto print_unsigned;

        case 'X':
            X = 1;
            truncate = doprnt_truncates;
            base = 16;
            goto print_unsigned;

        case 'r':
            truncate = doprnt_truncates;
        case 'R':
            base = radix;
            goto print_signed;

        case 'n':
            truncate = doprnt_truncates;
        case 'N':
            base = radix;
            goto print_unsigned;

print_signed:
            n = va_arg (args, long);
            if (n >= 0)
            {
                u = n;
                sign_char = plus_sign;
            }
            else
            {
                u = -n;
                sign_char = '-';
            }
            goto print_num;

print_unsigned:
            u = va_arg (args, unsigned long);
            goto print_num;

print_num:
            {
                char        buf[MAXBUF];    /* build number here */
                register char * p = &buf[MAXBUF-1];
                static char digitsx[] = "0123456789abcdef";
                static char digitsX[] = "0123456789ABCDEF";
                const char* digits = X ? digitsX : digitsx;
                char *prefix = 0;

                if (truncate)
                    u = (long)((int)(u));

                if (u != 0 && altfmt)
                {
                    if (base == 8)
                        prefix = "0";
                    else if (base == 16)
                        prefix = "0x";
                }

                do
                {
                    *p-- = digits[u % base];
                    u /= base;
                    prec--;
                } while (u != 0);

                length -= (&buf[MAXBUF-1] - p);
                if (sign_char)
                    length--;
                if (prefix)
                    length -= strlen(prefix);

                if (prec > 0)
                    length -= prec;
                if (padc == ' ' && !ladjust)
                {
                    /* blank padding goes before prefix */
                    while (--length >= 0)
                        if (!(*putc)(putc_arg, ' '))
                            goto L_EXIT;
                }
                if (sign_char)
                    if (!(*putc)(putc_arg, sign_char))
                        goto L_EXIT;
                if (prefix)
                    while (*prefix)
                        if (!(*putc)(putc_arg, *prefix++))
                            goto L_EXIT;

                while (--prec >= 0)
                    if (!(*putc)(putc_arg, '0'))
                        goto L_EXIT;

                if (padc == '0')
                {
                    /* zero padding goes after sign and prefix */
                    while (--length >= 0)
                        if (!(*putc)(putc_arg, '0'))
                            goto L_EXIT;
                }
                while (++p != &buf[MAXBUF])
                    if (!(*putc)(putc_arg, *p))
                        goto L_EXIT;

                if (ladjust)
                {
                    while (--length >= 0)
                        if (!(*putc)(putc_arg, ' '))
                            goto L_EXIT;
                }
                break;
            }

        case '\0':
            fmt--;
            break;

        default:
            if (!(*putc)(putc_arg, *fmt))
                goto L_EXIT;
        }
        fmt++;
    }
    (*putc)(putc_arg, '\0');

L_EXIT:
    return;
}

/* This version of printf is implemented in terms of PAL_putwchar.  */

#define PRINTF_BUFMAX   128

struct printf_state {
    Aura_Char* str;
    SIZE_T str_index;
    SIZE_T str_size;
    Aura_WChar buf[PRINTF_BUFMAX];
    SIZE_T index;
};

EXTERN_C Aura_Int32 __cdecl
Aura_vprintf(
      /* [in]  */ const Aura_PChar ss,
      /* [in]  */ va_list ap);

EXTERN_C Aura_Int32 __cdecl
Aura_printf(
      /* [in]  */ const Aura_PChar fmt, ...)
{
    va_list args;
    int len;

    va_start (args, fmt);
    len = Aura_vprintf (fmt, args);
    va_end (args);

    return len;
}


static bool
__flush (struct printf_state *state)
{
    unsigned int i;
    if (state->str)
    {
        int len;
        if (state->str_index >= state->str_size)
            return false;
        len = Aura_Wcstombs(state->str + state->str_index,
                    state->buf,
                    state->str_size - state->str_index);

        if (len <= 0) {
            return false;
        }
        state->str_index += len;
    }
    else
    {
        for (i = 0; i < state->index; ++i)
        {
            putwchar ((wchar_t)state->buf[i]);
            ++(state->str_index);
        }
    }

    state->index = 0;
    return true;
}

static bool
printf_char (void* arg, int c)
{
    bool res = true;
   struct printf_state *state = (struct printf_state *) arg;

    if (c == '\n')
    {
        state->buf[state->index] = 0;
        res = __flush (state);
        state->buf[state->index] = c;
        state->index++;
    }
    else if ((c == 0) || (state->index >= PRINTF_BUFMAX))
    {
        res = __flush (state);
        if (res)
        {
            if (state->str)
                state->str[state->str_index++] = c;
        }
    }
    else
    {
        state->buf[state->index] = c;
        state->index++;
    }
    return res;
}

EXTERN_C Aura_Int32 __cdecl
Aura_vprintf(
      /* [in]  */ const Aura_PChar ss,
      /* [in]  */ va_list ap)
{
    struct printf_state printf_state;

    printf_state.str = 0;
    printf_state.str_index = 0;
    printf_state.str_size = 0;
    printf_state.index = 0;

    doprnt (ss, ap, 0, printf_char, &printf_state);

    if (printf_state.index != 0)
        __flush (&printf_state);

    return printf_state.str_index;
}



EXTERN_C int CDECL __vswfprintf(Aura_PWChar d, Aura_WChar const *ss, va_list ap)
{
    const Aura_WChar    *t;
    Aura_WChar          *p, *dst, *ptmp;
    char                tmp[40];
    unsigned int        n;
    int                 fmt, trunc, haddot, width,longlong,base=0;
    int                 ctmp;
    Aura_WChar          wtmp[40];
    Aura_WChar const *  s = ss;
    Aura_Char *cp;

    dst = d;
    *d = 0;
    for (; *s;) {
        if (*s == '%') {
            s++;
            fmt = FMT_RJUST;
            width = trunc = haddot = longlong = 0;
            for (; *s; s++) {
                if (*s < 0x80 && strchr ("dobxXulsScefg%", (char)(*s)))
                    break;
                else if (*s == '-')
                    fmt = FMT_LJUST;
                else if (*s == '0')
                    fmt = FMT_RJUST0;
                else if (*s == '~')
                    fmt = FMT_CENTER;
                else if (*s == '*') {
                    if (haddot)
                        trunc = va_arg(ap, int);
                    else
                        width = va_arg (ap, int);
                }
                else if (*s >= '1' && *s <= '9') {
                    for (t = s; isdigit((char)(*s)); s++);
                    for (ctmp = 0; ctmp<s-t; ctmp++){
                        tmp[ctmp] = t[ctmp];
                    }
                    tmp[s - t] = '\0';
                    AnsiToUnic(wtmp, tmp);
                    Aura_Atob (&n, wtmp, 10);
                    if (haddot)
                        trunc = n;
                    else
                        width = n;
                    s--;
                }
                else if (*s == '.')
                    haddot = 1;
            }
            if (*s == '%') {
                *d++ = '%';
                *d = 0;
            }
            else if (*s == 'S') {
                cp = va_arg (ap, Aura_Char *);

                if (cp){
                    ptmp = (Aura_WChar*)malloc((strlen(cp)+1)*sizeof(Aura_WChar));
                    if(ptmp == NULL){
                        AnsiToUnic(wtmp, "(error)");
                        Aura_Wcscpy (d, wtmp);
                    }
                    else
                    {
                        AnsiToUnic(ptmp, cp);
                        Aura_Wcscpy (d, ptmp);
                        free(ptmp);
                        ptmp = NULL;
                    }
                }
                else{
                    AnsiToUnic(wtmp, "(null)");
                    Aura_Wcscpy (d, wtmp);
                }
            }
            else if (*s == 's') {
                p = va_arg (ap, Aura_WChar *);
                AnsiToUnic(wtmp, "(null)");
                if (p)
                    Aura_Wcscpy (d, p);
                else
                    Aura_Wcscpy (d, wtmp);
            }
            else if (*s == 'c') {
                n = va_arg (ap, int);

                *d = n;
                d[1] = 0;
            }
            else {
                if (*s == 'l') {
                    if (*++s == 'l') {
                        longlong = 1;
                        ++s;
                    }
                }
                AnsiToUnic(wtmp, "dobxXu");
                if (Aura_wcschr (wtmp, *s)) { //
                    if (*s == 'd')
                        base = -10;
                    else if (*s == 'u')
                        base = 10;
                    else if (*s == 'x' || *s == 'X')
                        base = 16;
                    else if (*s == 'o')
                        base = 8;
                   else if (*s == 'b')
                        base = 2;
                    Aura_Wbtoa (d, va_arg(ap, int), base); //

                    if (*s == 'X')
                        __wstr2upper (d);
                }
            }
            if (trunc)
                d[trunc] = 0;
            if (width)
                __wstr_fmt (d, width, fmt);
            for (; *d; d++);
            if (*s)
                s++;
        }
        else
            *d++ = *s++;
    }
    *d = 0;
    return (d - dst);
}

EXTERN_C Aura_Int32 __cdecl
Aura_swprintf(
      /* [in] */  Aura_PWChar buf,
      /* [in] */  Aura_WChar const *fmt, ...)
{
    Aura_Int32 uLength;
    va_list ap;

    va_start(ap, fmt);
    uLength = __vswfprintf(buf, fmt, ap);
    va_end(ap);

   return uLength;
}

EXTERN_C Aura_WChar* CDECL
Aura_fullpath(
    /* [in]  */ Aura_WChar *fullPath,
    /* [in]  */ Aura_WChar *curPath,
    /* [in]  */ Aura_Int32 n_Size)
{
    char szFullPath[AURA_MAX_DIR];
    char szCurPath[AURA_MAX_DIR];

    if(0 > n_Size){
        PERROR("Error: Aura_fullpath failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return NULL;
    }

    if(n_Size > AURA_MAX_DIR){
        return NULL;
    }

    UnicToAnsi(szCurPath, curPath);

    if(NULL == realpath(szCurPath, szFullPath)){
        return NULL;
    }

    if(strlen(szFullPath) >= (Aura_UInt32)n_Size){
        return NULL;
    }

    AnsiToUnic(fullPath, szFullPath);

    return fullPath;
}

EXTERN_C Aura_PWChar __cdecl
Aura_wcschr(const Aura_PWChar str, Aura_WChar c)
{
    Aura_PWChar s = str;
    while(*s && *s != c)s++;
    if(*s)return s;
    return NULL;
}

EXTERN_C Aura_PWChar __cdecl
Aura_wcsrchr(const Aura_PWChar str, Aura_WChar c)
{
    Aura_PWChar p = NULL;
    Aura_PWChar s = str;
    while(*s ){
        if(*s == c) p = s;
        s++;
    }
    return p;
}

EXTERN_C Aura_Int32 __cdecl Aura_getchar()
{
    return getchar();
}

EXTERN_C Aura_PVoid __cdecl
Aura_memcpy(Aura_PVoid dest, const Aura_PVoid src, Aura_Int32 count)
{
    return memcpy(dest, src, (int)count);
}
